package analisadorSintatico;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import util.ArquivoUtil;
import analisadorLexico.TipoToken;
import analisadorLexico.Token;

/**
 * Classe para validar a fase sint�tica da gram�tica
 * 
 * @author Alan L Cardoso
 * 
 */
public class AnalisadorSintatico {

	private List<Token> tokens;

	private List<String> erros = new ArrayList<String>();

	private Integer pToken = 0;

	private Token tokenAtual;

	private Node raiz;

	/**
	 * Método para Exibir a lista de erros.
	 */

	/**
	 * @param Lista
	 *            de Tokens
	 */
	public AnalisadorSintatico(List<Token> tokens) {
		this.tokens = tokens;
	}

	/**
	 * Método para Pega o Próximo Token Adicionar na Tabela de Símbolos
	 */
	private void nextToken() {

		if (this.tokens.size() > this.pToken) {
			this.tokenAtual = this.tokens.get(this.pToken);
			this.pToken++;
		}
	}

	/**
	 * @return Próximo token, a partir do token atual
	 * 
	 *         private Token getLookAhead() { return tokens.get(pToken); }
	 */

	/**
	 * Método para exibir árvore
	 */
	public void mostraArvore() {
		mostraNode(raiz, "   ");

	}

	/**
	 * @param node
	 * @param espaco
	 */
	private void mostraNode(Node node, String espaco) {
		System.out.println(espaco + node);
		if (node != null && node.getFilhos().size() > 0) {
			for (Node filho : node.getFilhos()) {
				mostraNode(filho, espaco + "   ");
			}
		}
	}

	public void analisar(String caminhoArquivoLog, PrintWriter arquivoLog) throws IOException {

		if (!analisar()) {

			ArquivoUtil.escreverArquivo("Foram Detectado Erros Durante a Análise Sintática.\n\n", arquivoLog);
			ArquivoUtil.escreverArquivo("== == == Início da Lista de Erros == == ==\n\n", arquivoLog);

			for (String erro : this.erros) {
				ArquivoUtil.escreverArquivo(erro + "\n", arquivoLog);
			}

			ArquivoUtil.escreverArquivo("\n== == == Fim da Lista de Erros == == ==\n\n", arquivoLog);
			ArquivoUtil.fecharArquivo(arquivoLog);
			System.out.println("Favor analisar o arquivo log.txt ");
			System.exit(0);
		}
	}

	/**
	 * Método para iniciar a análise sintática
	 * 
	 * @param arquivoLog
	 * @param caminhoArquivoLog
	 * 
	 * @return false or true
	 */
	private boolean analisar() {

		nextToken();
		this.raiz = listaRegras();

		if (this.tokenAtual.getImagem().equals("$") && erros.isEmpty()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Método que analisa a lista de regras
	 */

	// <listaRegra> ::= <regra> <listaRegra> | E
	private Node listaRegras() {

		Node node = new Node(TipoNode.LISTAREGRAS);

		if (this.tokenAtual.getClasse() == TipoToken.NAO_TERMINAL) {
			node.addFilho(regra());
			node.addFilho(listaRegras());
		}

		return node;
	}

	/**
	 * Método que analisa a Regra
	 */

	// <regra> ::= NT '::=' <listaLadoDireito>
	private Node regra() {

		Node node = new Node(TipoNode.REGRA);

		if (this.tokenAtual.getClasse() == TipoToken.NAO_TERMINAL) {
			node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
			nextToken();
			if (tokenAtual.getImagem().equalsIgnoreCase("::=")) {
				// malopes
				node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
				// malopes
				nextToken();
				node.addFilho(listaLadoDireito());
				if (this.tokenAtual.getImagem().equalsIgnoreCase(";")) {
					// malopes
					node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
					// malopes
					nextToken();
				} else {
					erros.add("Esperado Token ';'. Linha/Coluna: " + this.tokenAtual.getLinha() + "/"
							+ this.tokenAtual.getColuna());
				}
			}
		}
		return node;
	}

	/**
	 * Método que analisa a lista de lado direito
	 */

	// <listaLadoDireito> :: <ladoDireito> '|' <listaLadoDireito> | E
	private Node listaLadoDireito() {

		Node node = new Node(TipoNode.LISTALADODIREITO);

		node.addFilho(ladoDireito());

		if (this.tokenAtual.getImagem().equalsIgnoreCase("|")) {
			node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
			nextToken();
			node.addFilho(listaLadoDireito());
		}

		return node;
	}

	/**
	 * Método que analisa o lado direito da lista de lados direitos
	 */

	// <ladoDireito> ::= <simbolo> <ladoDireito> | E
	private Node ladoDireito() {

		Node node = new Node(TipoNode.LADODIREITO);

		if (this.tokenAtual.getClasse() == TipoToken.NAO_TERMINAL
				|| this.tokenAtual.getClasse() == TipoToken.TERMINAL_LITERAL) {
			node.addFilho(simbolo());
			node.addFilho(ladoDireito());
		}

		if (this.tokenAtual.getClasse() == TipoToken.TERMINAL_CLASSE) {
			node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
			nextToken();
			node.addFilho(ladoDireito());
		}

		return node;
	}

	/**
	 * Método que analisa o simbolo
	 */

	// <simbolo> ::= Terminal | NaoTerminal
	private Node simbolo() {

		Node node = new Node(TipoNode.SIMBOLO);

		if (this.tokenAtual.getClasse() == TipoToken.NAO_TERMINAL
				|| this.tokenAtual.getClasse() == TipoToken.TERMINAL_LITERAL) {
			node.addFilho(new Node(TipoNode.TOKEN, this.tokenAtual));
			nextToken();

		} else {
			erros.add("Esperado Token <NAO_TERMINAL> ou <TERMINAL>. Linha/Coluna: " + this.tokenAtual.getLinha() + "/"
					+ this.tokenAtual.getColuna());
		}

		return node;
	}

	/**
	 * Gets and Sets
	 */
	public Node getRaiz() {
		return raiz;
	}

	public void setRaiz(Node raiz) {
		this.raiz = raiz;
	}

}
